GCD全称是 Grand Central Dispatch,它是基于 C 语言开发的一套多线程开发机制,也是目前苹果官方推荐的多线程开发方法。
GCD的优势
- GCD 是苹果公司为多核的并行运算提供的解决方案;
- GCD 会自动利用更多的CPU内核(比如双核、四核);
- GCD 会自动管理线程的生命周期(创建任务、调度任务、销毁任务);
因此,程序员只需要告诉 GCD 想要执行什么任务,把任务放在对应的 block
里面。不需要编写任何线程管理代码。
GCD在工程中的位置
GCD 存在于 libdispatch.dylib
这个库里面,这个调度库包含了 GCD 的所有的东西,任何 iOS 程序默认就加载了这个库,因此不需要我们手动导入。
同步和异步的概念
同步:就是在执行一个任务时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。
异步:异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。
同步就是在同一个线程里,如果函数没有返回,线程就一直等。异步是另起一条线程来处理这个工作。
同步函数和异步函数
下面是同步函数的表达式:
|
|
下面是异步函数的表达式:
|
|
下面是两个函数的传入参数:
参数 | 作用 | 传入值 |
---|---|---|
queue: dispatch_queue_t | 这是一个队列 | 队列 |
_ block: dispatch_block_t | 这是我们要执行的一个任务 | block |
同步函数不会创建新的线程,当我们执行 dispatch_sync
这个函数时 它会一直等待 block
的完成,block
执行完这个函数才返回。
当执行 dispatch_async
时,不论 block 是否执行完,只要调用了函数就返回了。
下面讲几个例子来帮助我们理解同步和异步:
同步:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。
异步:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。同步:提交请求->等待服务器处理->处理完毕返回 这个期间客户端不能干任何事
异步:请求通过事件触发->服务器处理(这时客户端仍然可以做其他事情)->处理完毕同步:就是你叫我去吃饭,我听到了就和你去吃饭;如果没有听到,你就不停的叫,直到我告诉你我听到了,才一起去吃饭。
异步就是你叫我,然后自己去吃饭,我得到消息后可能立即走,也可能等到下班才去吃饭。所以,要我请你吃饭就用同步的方法,要请我吃饭就用异步的方法。
同步函数和异步函数的使用场景
|
|
CGD的 3 种队列
使用 GCD 开发基本看不到线程,线程的细节都被封装起来了,我们直接把要执行的任务添加到队列里就可以了,GCD 会根据指定的队列 ,将队列里的任务取出来放在对应的线程执行。任务的取出遵循队列的 FIFO 原则:先进先出,后进后出。
队列分为两种,一种是串行队列,一种是并行队列。但是具体到GCD里面,队列就分为了 3 种。
主队列
主队列是指运行在主线程里的
Main Queue
, 主队列不需要创建,可以通过dispatch_get_main_queue
获取到主队列。并行队列
并行队列是一种全局并行队列,同样不需要自己创建,通过
dispatch_get_global_queue
可以获取到全局的并行队列,而且这个全局的并行队列有3个,因为有 3个不同的优先级 。我们可以通过使用dispatch_get_main_queue
获取全局并行队列,在获取时可以通过指定它的优先级,来获取不同优先级的并行队列。并行队列的执行顺序和加入队列的顺序相同,也就是先入先出的原则。自定义队列
自定义队列就是我们自己创建的队列,我们可以通过
dispatch_queue_creat
进行创建,创建自定义队列时可以声明其是串行队列还是并行队列。
同步、异步、并行、串行的概念比较
同步和异步决定了要不要开启新的线程:
同步:在当前进行中执行任务,不具备开启新线程的能力;
异步:在新的线程中执行任务,具备开启新线程的能力;
并行和串行决定了任务执行的方式
并行:多个任务并发(同时)执行;
串行:一个任务执行完毕后,再执行下一个任务;
使用异步函数执行主队列
|
|
通过打印日志,我们可以知道主队列是在主线程中执行。
使用异步函数执行并行队列
获取并行队列
|
|
传入参数如下:
形参 | 作用 | 传入值 |
---|---|---|
identifier: Int | 表示队列的优先级。 | 见下一个表格 |
_ flags: UInt | 为以后使用的标记,传入0以外的值可能会返回空值。 | 0 |
identifier: Int
在 iOS7
以后可以使用的值:
值的名称 | 含义:使用场景 |
---|---|
QOS_CLASS_USER_INTERACTIVE | 用户交互:尽快完成,用户在期待结果,不要放太耗时操作。 |
QOS_CLASS_USER_INITIATED | 用户期望:不要放太耗时操作。 |
QOS_CLASS_DEFAULT | 默认:不是给程序员使用的,用来重置对列使用的。 |
QOS_CLASS_UTILITY | 实用工具:耗时操作,可以使用这个选项。 |
QOS_CLASS_BACKGROUND | 后台 |
identifier: Int
在 iOS7
以前使用的值:
值的名称 | 对应关系 | 优先级 |
---|---|---|
DISPATCH_QUEUE_PRIORITY_HIGH | QOS_CLASS_USER_INITIATED | 高优先级 |
DISPATCH_QUEUE_PRIORITY_DEFAULT | QOS_CLASS_DEFAULT | 默认优先级 |
DISPATCH_QUEUE_PRIORITY_LOW | QOS_CLASS_UTILITY | 低优先级 |
DISPATCH_QUEUE_PRIORITY_BACKGROUND | QOS_CLASS_BACKGROUND | 后台优先级 |
值得注意的是:BACKGROUND
表示用户不需要知道任务什么时候完成,如果选择这个选项速度慢得令人发指,非常不利于调试!对于优先级推荐不要搞得太负责,就用最简单,以免发生优先级反转。
下面我们创建 2 个异步函数并执行一个并行队列:
|
|
通过打印日志我们可以知道:如果使用全局并行队列,并且使用异步函数执行时,异步函数会创建一个新的并行线程,并行线程和主线程是同时运行的,所以哪个先执行并不能掌握。
每当有一个异步函数执行并行队列,都会创建一个新的子线程,并在这个新的线程中运行。但这并不意味着你使用很多异步函数执行并行队列,就会有很多个子线程,当这种情况发生时,系统会将你在 block 的操作封装给其他的子线程运行。
使用异步函数执行自定义队列
创建自定义队列
|
|
传入参数如下:
形参 | 作用 | 传入值 |
---|---|---|
label: UnsafePointer |
队列的名称 | 字符串,一般是请求的操作,可以是nil。 |
_ attr: dispatch_queue_attr_t! | 队列的类型 | 串行队列:DISPATCH_QUEUE_SERIAL 并行队列:DISPATCH_QUEUE_CONCURRENT |
下面我们创建 2 个异步函数并执行一个串行行队列:
|
|
通过打印结果,我们可以知道:如果使用串行队列,并且是异步函数时,不论有多少异步函数,只会创建一个新的线程。
异步函数执行不同队列的对比
异步函数 | |
---|---|
获取的主队列 | 没有开启新线程,串行执行任务。 |
获取的并行队列 | 有开启新线程且可多开线程,并行执行任务。 |
创建的并行队列 | 有开启新线程但只可开一条,串行执行任务。 |